require "light"
require "vector"
require "loveplus"
require "view"

local function diffuse(vertex, normal) --normal is expeced to be an unit vector
  local ray=vertex-light.position
  local distance=vector.abs(ray)
  if distance<1 then return 1 end
  local flux=vector.dot(normal,vector.unit(ray))
  if flux<0 then return 0 end --no backface lighting
return math.min(1,flux/math.sqrt(math.sqrt(distance))) end

local function specular(vertex, normal)
  local ray=vertex-light.position
  local sense=view.eye()-vertex
  local reflex=ray-2*vector.project(ray,normal)
  local dot=vector.dot(reflex, sense)
  if dot<0 then return 0 end
  sense=vector.dot(sense,sense)
  reflex=vector.dot(reflex,reflex)
  local spec=dot*dot/sense/reflex
  
return spec*spec end

function metallic(color,vertex,normal)
  local d=diffuse(vertex,normal)
  local s= d>0 and specular(vertex,normal)
  color=love.graphics.blendColors(color, 0, (1+d*2)/3)
  if s then color=love.graphics.blendColors(color,0xFFFFFF,1-s/2) end
return color end
--face:
  -- 1..# as vectors
  -- normal as a unit length vector
  -- center as a vector
  -- color as number

  
function renderSurface(surface)
  
  local cx,cy,cz=view.project(surface.center+surface.object.position)
  local color=metallic(surface.color or surface.object.color or 0xAAAAAA,surface.center+surface.object.position,surface.normal)
  local sense=view.eye()-surface.center-surface.object.position
  if vector.dot(sense,surface.normal)>0 then return end --backface culling

  local polygon=surface.polygon
  while polygon do
    local pts={}
    for i,v in ipairs(polygon) do
      local x,y=view.project(v+surface.object.position)
      table.push(pts,x,y)
    end
    love.graphics.setColor(color)
    love.graphics.polygon("fill", unpack(pts))
    polygon=polygon.next
  end
  
  local texture=surface.texture
  while texture do
    local img=love.graphics.newImage(texture.image)
    local ox,oy=view.project(texture.o+surface.object.position)
    local xx,xy=view.project(texture.x+surface.object.position)
    local yx,yy=view.project(texture.y+surface.object.position)
    love.graphics.setColor(surface.textureColor or surface.object.textureColor or 0xFFFFFF)
    love.graphics.push()
    love.graphics.transform(ox,oy,xx,xy,yx,yy)
    love.graphics.draw(img,0,0,0,2/img:getWidth(),2/img:getHeight(),img:getWidth()/2,img:getHeight()/2)
    love.graphics.pop()
    texture=texture.next
  end
  
end

function renderShadow(object)
  
  local polygon={}
  for i, vertex in ipairs(object.vertices) do
    local vertex=vertex+object.position
    if vertex[3]<light.position[3] then
      local shadow=(vertex-light.position)*light.position[3]/(light.position[3]-vertex[3])+light.position
      shadow[3]=0
      local x,y = view.project(shadow)
      table.insert(polygon, Vector{x,y,0})
    end
  end
    
  --convex hull, gift wrapping algorithm
  --find the leftmost point
  --thats in the hull for sure
  if #polygon<3 then return end
  local hull={polygon[1]}
  for i=1,#polygon do if polygon[i][1]<hull[1][1] then hull[1]=polygon[i] end end
  
  --now wrap around the points to find the outermost
  --this algorithm has the additional niceity that it gives us the points clockwise
  --which is important for love.polygon
  repeat
    local point=hull[#hull]
    local endpoint=polygon[1]
    if point==endpoint then endpoint=polygon[2] end
    
    --see if polygon[i] is to the left of our best guess so far
    for i=1,#polygon do
      local left = endpoint-point
      left[1],left[2]=left[2],-left[1]
      local diff=polygon[i]-endpoint
      if vector.dot(diff,left)>0 then
        endpoint=polygon[i]
      end
    end
    hull[#hull+1]=endpoint
    if #hull>#polygon+1 then return end --we've done something wrong here
  until hull[1]==hull[#hull]
  if #hull<3 then return end --also something wrong or degenerate case
  local args={}
  for i,pt in ipairs(hull) do table.push(args,pt[1],pt[2]) end
  love.graphics.setColor(0,0,0,230)
  love.graphics.polygon("fill",unpack(args))
end  


